/*
 * Copyright 2011-2012 by The Regents of the University of California Licensed
 * under the Apache License, Version 2.0 (the "License"); you may not use this
 * file except in compliance with the License. you may obtain a copy of the
 * License from
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */
package edu.ucsd.db.bassarisk.storage.loading;

import java.nio.ByteBuffer;
import java.nio.IntBuffer;

import edu.ucsd.db.bassarisk.data.api.IValueType.IValueReference;
import edu.ucsd.db.bassarisk.data.values.IntType;
import edu.ucsd.db.bassarisk.data.values.IntType.IntTools;
import edu.ucsd.db.bassarisk.data.values.IntType.IntegerValueCodec;
import edu.ucsd.db.bassarisk.data.values.IntType.ObjectIntegerReference;
import edu.ucsd.db.bassarisk.storage.common.ColumnStore;

/**
 * Used to bulk load a sequence of 32-bit integers into a column in the
 * Bassarisk column store database.
 * 
 * This class loads a single Card with a simple fixed-width integer
 * serialization.
 * 
 * @author nbales
 */
public class FixedLengthIntegerCardLoader implements ICardLoader<IntType> {

  /**
   * An integer posting reference instance that can interpret generic
   * IPostingReference values which will be appended to the card.
   */
  private final IntegerValueCodec integerValue;

  /**
   * A card-sized temporary buffer which
   */
  private final ByteBuffer buffer;
  private final IntBuffer intBufferView;

  private final ColumnSegmentInfo<IntType> cardInfo;

  /**
   * Create a new FixedIntegerCardLoader and initialize final fields.
   */
  public FixedLengthIntegerCardLoader() {
    super();
    this.integerValue = IntTools.INSTANCE.getGenericCodec();
    this.buffer = ByteBuffer.allocate(ColumnStore.BYTES_IN_CARD);
    this.intBufferView = this.buffer.asIntBuffer();
    this.cardInfo = new ColumnSegmentInfo<IntType>(IntTools.INSTANCE);
  }

  @Override
  public boolean appendBufferedValue(final IValueReference<IntType> value) {
    // Check to see if this value will fit on the current card.
    if (this.intBufferView.position() >= this.intBufferView.limit()) { return false; }

    this.cardInfo.incrementEntryCount();

    // Move the value from source to card.
    int intValue = this.integerValue.getIntegerValue(value);
    this.intBufferView.put(intValue);
    return true;
  }

  @Override
  public boolean fileCard(final DrawerLoader<IntType> drawer) {
    // Give the minimum value on the card to the minValue object.
    this.cardInfo
        .setFirstValue(new ObjectIntegerReference(this.intBufferView.get(0)));
    this.cardInfo.setLastValue(new ObjectIntegerReference(this.intBufferView
        .get((int) this.cardInfo.getEntryCount() - 1)));

    // Succeeds if-and-only-if drawer append succeeds.
    return drawer.appendCard(this.buffer.array(), this.cardInfo);
  }

  @Override
  public byte[] getCardBytes() {
    return this.buffer.array();
  }

  @Override
  public void initialize() {
    this.reset(0);
  }

  @Override
  public void reset(final int cardStartEntry) {
    this.buffer.clear();
    this.intBufferView.clear(); // is this necessary?
    this.cardInfo.reset(cardStartEntry);
  }

}
